﻿//========================================================= 
/**@file DynamicDriverLinker.h
 * @brief 兼容Linux so和Windows dll的动态库加载器
 * 
 * @date 2016-07-31   09:55:34
 * @author Zhyioo 
 * @version 1.0
 */ 
//--------------------------------------------------------- 
#ifndef _LIBZHOUYB_DYNAMICDRIVERLOADER_H_
#define _LIBZHOUYB_DYNAMICDRIVERLOADER_H_
//--------------------------------------------------------- 
#include "../../include/Base.h"

#ifdef _MSC_VER
#include <DbgHelp.h>
#pragma comment(lib, "DbgHelp.lib")
#else
#include <dlfcn.h>
#endif
//--------------------------------------------------------- 
namespace zhou_yb {
namespace application {
namespace tools {
//--------------------------------------------------------- 
#ifdef _MSC_VER
//--------------------------------------------------------- 
/// 屏蔽系统间加载释放的差异
class DllImport
{
public:
    typedef HMODULE handle_type;
protected:
    DllImport() {}
public:
    static inline handle_type Null()
    {
        return NULL;
    }
    static handle_type Load(const char* drv)
    {
        CharConverter cvt;
        HMODULE hDll = ::LoadLibrary(cvt.to_char_t(drv));
        return hDll;
    }
    static pointer Get(handle_type& hdll, const char* entryPoint)
    {
        return GetProcAddress(hdll, entryPoint);
    }
    static inline bool IsValid(handle_type hdll)
    {
        return hdll != Null();
    }
    static inline void Free(handle_type& hdll)
    {
        ::FreeLibrary(hdll);
        hdll = Null();
    }
};
#else
/// 屏蔽系统间加载释放的差异
class DllImport
{
public:
    typedef pointer handle_type;
protected:
    DllImport() {}
public:
    static inline handle_type Null()
    {
        return NULL;
    }
    static inline handle_type Load(const char* drv)
    {
        return dlopen(drv, RTLD_LAZY);
    }
    static pointer Get(handle_type& hdll, const char* entryPoint)
    {
        return dlsym(hdll, entryPoint);
    }
    static inline bool IsValid(handle_type hdll)
    {
        return hdll != Null();
    }
    static inline void Free(handle_type& hdll)
    {
        dlclose(hdll);
        hdll = Null();
    }
};
#endif
//--------------------------------------------------------- 
/// 动态库加载器
class DynamicDriverLinker : public RefObject
{
public:
    //----------------------------------------------------- 
#ifdef _MSC_VER
    /**
     * @brief 枚举DLL中的导出函数 
     * 
     * @param [in] dllPath DLL文件路径 
     * @param [out] dllNames 枚举到的DLL导出函数列表 
     * 
     * @return size_t 枚举到的函数数目 
     */
    static size_t EnumFunction(const char* dllPath, list_t<string>& dllNames)
    {
        size_t count = 0;

        WIN32_FIND_DATA wfd;
        CharConverter cvt;
        memset(&wfd, 0, sizeof(wfd));

        if(FindFirstFile(cvt.to_char_t(dllPath), &wfd) == NULL)
            return count;

        HANDLE hFile = CreateFile(cvt.to_char_t(dllPath), GENERIC_READ, 0, NULL, OPEN_EXISTING, wfd.dwFileAttributes, NULL);
        if(hFile == NULL || hFile == INVALID_HANDLE_VALUE)
            return count;

        HANDLE hFileMap = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
        if(hFileMap == NULL || hFileMap == INVALID_HANDLE_VALUE)
        {
            CloseHandle(hFile);
            return count;
        }

        void* modeBase = MapViewOfFile(hFileMap, FILE_MAP_READ, 0, 0, 0);
        if(modeBase != NULL)
        {
            IMAGE_DOS_HEADER* pDosHeader = (IMAGE_DOS_HEADER*)modeBase;
            if(pDosHeader == NULL)
                return count;
            IMAGE_NT_HEADERS* pNtHeader = (IMAGE_NT_HEADERS*)((byte*)modeBase + pDosHeader->e_lfanew);
            IMAGE_OPTIONAL_HEADER* pOptHeader = (IMAGE_OPTIONAL_HEADER*)((byte*)modeBase + pDosHeader->e_lfanew + 24);
            if(pOptHeader == NULL)
                return count;
            IMAGE_EXPORT_DIRECTORY* pExportDesc = (IMAGE_EXPORT_DIRECTORY*)ImageRvaToVa(pNtHeader, modeBase, pOptHeader->DataDirectory[0].VirtualAddress, 0);
            if(pExportDesc == NULL)
                return count;
            PDWORD nameAdr = (PDWORD)ImageRvaToVa(pNtHeader, modeBase, pExportDesc->AddressOfNames, 0);
            if(nameAdr == NULL)
                return count;
            char_t* funcName = NULL;
            for(DWORD i = 0;i < pExportDesc->NumberOfNames; ++i)
            {
                funcName = (char_t*)ImageRvaToVa(pNtHeader, modeBase, (DWORD)nameAdr[i], 0);
                ++count;

                dllNames.push_back(cvt.to_char(funcName));
            }
        }

        CloseHandle(hFileMap);
        CloseHandle(hFile);

        return count;
    }
#endif
    //----------------------------------------------------- 
protected:
    shared_obj<DllImport::handle_type> _hDll;
public:
    DynamicDriverLinker()
    {
        _hDll.obj() = DllImport::Null();
    }
    DynamicDriverLinker(const char* drv)
    {
        _hDll.obj() = DllImport::Load(drv);
    }
    virtual ~DynamicDriverLinker()
    {
        Free();
    }
    bool Load(const char* drv)
    {
        Free();
        _hDll.obj() = DllImport::Load(drv);
        return IsValid();
    }
    inline DllImport::handle_type Handle() const
    {
        return _hDll.obj();
    }
    inline operator bool() const
    {
        return IsValid();
    }
    template<class T>
    T Get(const char* entryPoint)
    {
        return reinterpret_cast<T>(DllImport::Get(_hDll.obj(), entryPoint));
    }
    bool IsValid() const
    {
        return DllImport::IsValid(_hDll.obj());
    }
    void Free()
    {
        if(IsValid())
        {
            if(_hDll.ref_count() < 2)
            {
                DllImport::Free(_hDll.obj());
            }
            _hDll.reset() = DllImport::Null();
        }
    }
};
//--------------------------------------------------------- 
} // namespace tools
} // namespace application
} // namespace zhou_yb
//--------------------------------------------------------- 
#endif // _LIBZHOUYB_DYNAMICDRIVERLOADER_H_
//========================================================= 